/*

	$.override - jQuery Override Plugin

	Author:		Rod Woo
	Version:	1.0.1
	Released:	2012-03-17T19:17:00Z
	URL:		http://code.google.com/p/jquery-rwoo
	

Redefines methods in objects.

The general syntax is

    $.override(object, redefinedMethods). 

Example:

	In order to override the methods myObject.a() and 
	myObject.b(param1, param2) just call

	$.override(
		myObject, 
		{
			a : function($super) {
				... // you might call $super() here 
			},
			b : function($super, param1, param2) {
				... // you might call $super(x,y) here
			}
		}
	);

Examples (taken from src/test/javascript/jquery.override_examples.js):

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// EXAMPLE 1: Basic usage

var tokenizer = {
	tokenize : function(text) {
		return text.split(" ");
	}
};

// we expect three tokens: "jQuery", "is", "up-to-date"
expect(tokenizer.tokenize("jQuery is up-to-date").length).toBe(3);

// improve the tokenizer: hyphens shall be considered whitespace 
jQuery.override(tokenizer, {

	tokenize : function($super, text) {
		text = text.replace(/-/g, " ");
		return $super(text);
	}

});

// we expect five tokens: "jQuery", "is", "up", "to", "date"
expect(tokenizer.tokenize("jQuery is up-to-date").length).toBe(5);



// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// EXAMPLE 2: Change the behaviour of a slider widget.

// define a horizontal slider with a position
var mySlider = {

	position : 0,

	render : function(newPosition) {
		this.position = newPosition;
		// ... do the rendering now ...
	},

	mouseover : function(mouseEvent) {
		this.render(mouseEvent.pageX);
	}
};

// enhance the widget by redefining the method render()
jQuery.override(mySlider, {

	// the slider shall be positioned ten pixels to the right of the
	// mouse pointer
	render : function($super, newPosition) {
		// you should use $super without the keyword "this"
		$super(newPosition + 10);
	}

});

// send a mouse event to the slider
mySlider.mouseover({
	pageX : 400,
	pageY : 500
});
expect(mySlider.position).toBe(410);





*/

(function ($) {

	/** 
	 * @param originalMethod - the method to override. Required parameter.
	 * @param redefinedMethod - the method that redefines the original method. 
	 *        Required parameter.
	 * @return Returns a new method that that calls the redefined method and
	 *         delegates all calls to $super() within the redefined method to
	 *         the original method. 
	 */
	function override(originalMethod, redefinedMethod) {

		// validate the arguments
		if (arguments.length < 2 || originalMethod == null || redefinedMethod == null) {
			throw "missing arguments: " + originalMethod + " " + redefinedMethod;
		}

		var overriddenMethod = function() {
		
			// (1) bind the method of the super class to "this"
		    var self = this;
		    var boundOriginalMethod = function () {
				return originalMethod.apply(self, $.makeArray( arguments ) );
			};
			
			// (2) call the method of the subclass. the parameters are the 
			// method of the super class and the other arguments
			var args = $.makeArray(arguments);
			args.unshift(boundOriginalMethod);
			return redefinedMethod.apply(this, args);
		};
		
		// TODO in future we might add the toString or the valueOf functions
		// of the redefinedMethod to the overriddenMethod 
		return overriddenMethod;
	};

	/**
	 * Redefines methods in target. 
	 * 
	 * @param target
	 * @param redefinedMethods
	 * @return Returns the given target.
	 */
	$.override = function (target, redefinedMethods) {

		// (1) validate the arguments
		if (arguments.length < 2 || target == null || redefinedMethods == null) {
			throw "missing arguments: " + target + ", " + redefinedMethods;
		}
		for (var methodName in redefinedMethods) {

			// validate the type of the properties (they must be functions)
			if (typeof target[methodName] != "function" || 
				typeof redefinedMethods[methodName] != "function") {

				throw "property is missing or not of type 'function': " 
				  + methodName + ", " + target[methodName]
				  + ", " + redefinedMethods[methodName];
			}

		}

		// (2) replace the methods in the target with the overwritten methods
		for (var methodName in redefinedMethods) {

			// override the method at the target
			target[methodName] = override(target[methodName], 
					                redefinedMethods[methodName]);
		}
		
		return target;
	};
	
	
})(jQuery);

